package com.strongbj.iot.devices.newlight.response.handle;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.base.Preconditions;
import com.strongbj.core.message.IMessageHandle;
import com.strongbj.core.util.ByteUtil;
import com.strongbj.core.util.ContextUtils;
import com.strongbj.iot.devices.newlight.common.NetMapping;
import com.strongbj.iot.devices.newlight.entity.Reader;
import com.strongbj.iot.devices.newlight.entity.T;
import com.strongbj.iot.devices.newlight.message.NewLightOSMessageFactory;
import com.strongbj.iot.devices.newlight.message.NewLightReaderMessage;
import com.strongbj.iot.devices.newlight.response.common.HandleTimeStamp;
import com.strongbj.iot.devices.newlight.response.vo.ReaderChannel;
import com.strongbj.iot.devices.newlight.service.ReaderService;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;

public class ChannelHandle implements IMessageHandle<NewLightReaderMessage, Object> {
	private static final Logger logger = LogManager.getLogger(ChannelHandle.class);
	private static final byte CHANNEL_APPLY_TYPE = Integer.valueOf("31", 16).byteValue(); // 信道申请的类型
	private static final byte CHANNEL_DESTORY_TYPE = Integer.valueOf("32", 16).byteValue(); // 信道释放的类型
	private static final int WAIT_REQUEST_SECONDS = 5; // 等待请求时间
	private static final int DEVICE_MAX_WAIT_TIME = 25000; // 25秒

	private static ScheduledExecutorService threadPool = null;
	private static int channelHandleCount = 0;
	private static Map<String, ReaderChannel> channelMap = new ConcurrentHashMap<>();
	private ReaderService readerService = (ReaderService) ContextUtils.getBean("readerService");
	private NewLightOSMessageFactory newLightOSMessageFactory = (NewLightOSMessageFactory) ContextUtils
			.getBean("newLightOSMessageFactory");

	public ChannelHandle() {
		if (channelHandleCount++ == 0) {
			threadPool = Executors.newScheduledThreadPool(5);
		}
	}

	@Override
	public boolean isHandle(NewLightReaderMessage t) {
		return t.getType() == CHANNEL_APPLY_TYPE || t.getType() == CHANNEL_DESTORY_TYPE;
	}

	@Override
	public Object handle(ChannelHandlerContext ctx, NewLightReaderMessage t) {
		String hostNumber = ByteUtil.byteArrToHexString(t.getAddress()); // reader 地址码
		String timeStamp = ByteUtil.byteArrToHexString(t.getTime()); // 时间戳
		if (t.getType() == CHANNEL_APPLY_TYPE) {
			byte priority = t.getBody()[0]; // 申请优先级
			if(priority != 0x00 && priority != 0x01 && priority != 0x02 && priority != 0x03) {
				return null;
			}
			String cycle = ByteUtil.byteArrToHexString(t.getBody(), 1, 1); // 申请周期数
			String validity = ByteUtil.byteArrToHexString(t.getBody(), 2, 4); // 申请的秒数
			String log = "\r\n===================================================" + "\r\n收到信道状态查询申请报文："
					+ "\r\nREADER地址码：" + hostNumber + "\r\n申请优先级：" + priority + "\r\n申请周期数：" + cycle + "\r\n周期有效期 MS："
					+ Long.valueOf(validity, 16) + "\r\n时间戳：" + HandleTimeStamp.handle(timeStamp);
			logger.info(log);
			this.channelApplyHandle(ctx, t);
		} else if (t.getType() == CHANNEL_DESTORY_TYPE) {
			String log = "\r\n===================================================" + "\r\n收到信道状态释放报文："
					+ "\r\nREADER地址码：" + hostNumber + "\r\n时间戳：" + HandleTimeStamp.handle(timeStamp);
			logger.info(log);
			this.channelDestoryHandle(t);
		}
		return null;
	}

	@Override
	public void destory() {
		if (--channelHandleCount == 0 && threadPool != null) {
			threadPool.shutdown();
		}
	}

	/**
	 * 通道申请的处理
	 * 
	 * @param ctx
	 * @param t
	 */
	private void channelApplyHandle(ChannelHandlerContext ctx, NewLightReaderMessage t) {
		Reader reader = this.findReaderByCode(ByteUtil.bytesToAddress(t.getAddress()));
		Preconditions.checkNotNull(reader, "设备未在数据库中记录，无法判断此设备所在的机房。");
		Preconditions.checkNotNull(reader.getRoom(), "设备所在机房未在数据库中记录，无法判断此设备所在的机房。");

		byte priority = t.getBody()[0]; // 申请优先级

		if (ChannelHandle.channelMap.containsKey(reader.getRoom())) { // 如果机房在map中存在，说明在忙碌状态
			ReaderChannel rc = ChannelHandle.channelMap.get(reader.getRoom());
			if(ByteUtil.byteArrToHexString(t.getAddress()).equals(ByteUtil.byteArrToHexString(rc.getDeviceCode()))) {
				ReaderChannel newRc = createReaderChannel(t, priority);
				sendChannelReturnCommand(reader, t.getAddress(), null, newRc); // 发送通道空闲指令
				logger.info("有设备占用通道 占用设备编号:{} 但是申请的设备和以占用相同，已向设备发送空闲命令 新设备编号:{}",
						ByteUtil.byteArrToHexString(rc.getDeviceCode()), ByteUtil.byteArrToHexString(t.getAddress()));
			}else 
				if ((System.currentTimeMillis() - rc.getTime() > ByteUtil.byteArrayToInt(rc.getValidity()) * 1000
					|| System.currentTimeMillis() - rc.getTime() > DEVICE_MAX_WAIT_TIME)
					&& priority >= rc.getMaxPriority()) {
				ReaderChannel newRc = createReaderChannel(t, priority);
				sendChannelReturnCommand(reader, t.getAddress(), null, newRc); // 发送通道空闲指令
				logger.info("有设备占用通道 占用设备编号:{} 但已经超时，已向新申请设备发送空闲命令 新设备编号:{}",
						ByteUtil.byteArrToHexString(rc.getDeviceCode()), ByteUtil.byteArrToHexString(t.getAddress()));
			} else {
				rc.setMaxPriority((byte) Math.max(priority, rc.getMaxPriority()));
				sendChannelReturnCommand(reader, t.getAddress(), rc, null); // 发送通道非空闲指令
				logger.info("有设备占用通道 占用设备编号:{} 新设备发送非空闲命令 新设备编号:{}", ByteUtil.byteArrToHexString(rc.getDeviceCode()),
						ByteUtil.byteArrToHexString(t.getAddress()));
			}
		} else {
			ReaderChannel newRc = createReaderChannel(t, priority);
			sendChannelReturnCommand(reader, t.getAddress(), null, newRc); // 发送通道空闲指令
			logger.info("未发现设备占用通道 新设备编号:{}", ByteUtil.byteArrToHexString(t.getAddress()));
		}

	}

	/**
	 * 通道释放的处理
	 * 
	 * @param t
	 */
	private void channelDestoryHandle(NewLightReaderMessage t) {
		String deviceCode = ByteUtil.bytesToAddress(t.getAddress());
		Reader reader = this.findReaderByCode(deviceCode);
//		channelDestoryHandleByReader(reader,-1);
		ReaderChannel rc = channelMap.get(reader.getRoom());
		String curDeviceCode = ByteUtil.bytesToAddress(rc.getDeviceCode());
		Preconditions.checkArgument(deviceCode.equals(curDeviceCode),
				"申请释放信道的设备["+deviceCode+"]与已占用信道的设备["+curDeviceCode+"]不同");
		
		long time = System.currentTimeMillis()-1000000;
		rc.setTime(time);
		threadPool.schedule(new ChannelDestoryMessageRunnble(reader,time),
				WAIT_REQUEST_SECONDS, TimeUnit.SECONDS);
	}

	/**
	 * 根据Reader类中的信息释放通道
	 * 
	 * @param reader
	 */
	private void channelDestoryHandleByReader(Reader reader,long time) {
		Preconditions.checkNotNull(reader, "设备未在数据库中记录，无法判断此设备所在的机房。");
		Preconditions.checkNotNull(reader.getRoom(), "设备所在机房未在数据库中记录，无法判断此设备所在的机房。");

		if (ChannelHandle.channelMap.containsKey(reader.getRoom()) && 
				(time==ChannelHandle.channelMap.get(reader.getRoom()).getTime() || time==-1)) {
			logger.info("释放信道在内存中的备份数据 设备编号:{}", reader.getCode());
			ChannelHandle.channelMap.remove(reader.getRoom());
		}
	}

	/**
	 * 根据设备编号查找设备信息
	 * 
	 * @param code
	 * @return
	 */
	private Reader findReaderByCode(String code) {
		QueryWrapper<Reader> wrapper = new QueryWrapper<Reader>();
		wrapper.eq("code_", code);
		return readerService.getOne(wrapper);
	}

	/**
	 * 创建设备通道信息类
	 * 
	 * @param t
	 * @param priority
	 * @return
	 */
	private ReaderChannel createReaderChannel(NewLightReaderMessage t, byte priority) {
		ReaderChannel rc = new ReaderChannel();
		rc.setDeviceCode(t.getAddress());
		rc.setPriority(priority);
		if(priority == 0x00) {
			rc.setMaxPriority((byte) 1);
		}else {
			rc.setMaxPriority(priority);
		}	
		byte[] bValidity = new byte[4];
		System.arraycopy(t.getBody(), 2, bValidity, 0, 4);
		rc.setValidity(bValidity);
		rc.setCycle(t.getBody()[1]);
		rc.setTime(System.currentTimeMillis());
		return rc;
	}

	/**
	 * 发送信道反馈命令
	 * 
	 * @param reader
	 * @param deviceCode
	 * @param rc
	 */
	private void sendChannelReturnCommand(Reader reader, byte[] deviceCode, ReaderChannel rc, ReaderChannel newRc) {
		byte[] sendData;
		if (rc == null) {
			sendData = new byte[1];
			sendData[0] = (byte) 0x1; // 空闲
		} else {
			sendData = new byte[9];
			sendData[0] = (byte) 0x0; // 忙绿
			sendData[1] = rc.getCycle(); // 申请周期数
			System.arraycopy(rc.getDeviceCode(), 0, sendData, 1, 3); // 设备地址
			System.arraycopy(ByteUtil.intToByteArray((int) rc.getTime()), 0, sendData, 4, 4); // 时间戳
			sendData[8] = rc.getPriority();
		}
		byte[] req = newLightOSMessageFactory.createNewLightOSMessage(deviceCode, CHANNEL_APPLY_TYPE, sendData,
				T.getTimeStamp());
		ByteBuf bs = Unpooled.copiedBuffer(req);
		Channel channel = NetMapping.getInstance().getChannel(reader.getIp());
		ChannelFuture cf = channel.writeAndFlush(bs);
		cf.addListener(new ChannelFutureListener() {

			@Override
			public void operationComplete(ChannelFuture future) throws Exception {
				if (future.isSuccess()) {
					logger.info("发送信道命令成功 设备编号:{} 设备IP:{} 下发命令:{}", reader.getCode(), reader.getIp(),
							ByteUtil.byteArrToHexString(req,true));
					if (newRc != null) {
						ChannelHandle.channelMap.put(reader.getRoom(), newRc);
						threadPool.schedule(new ChannelDestoryMessageRunnble(reader,newRc.getTime()),
								ByteUtil.byteArrayToInt(newRc.getValidity()) + WAIT_REQUEST_SECONDS, TimeUnit.SECONDS);
					}
				} else {
					logger.debug("发送信道命令失败 设备编号:{} 设备IP:{} 下发命令:{}", reader.getCode(), reader.getIp(),
							ByteUtil.byteArrToHexString(req,true));
				}
			}

		});
	}

	/**
	 * 通道释放处理
	 * 
	 * @author yuzhantao
	 *
	 */
	class ChannelDestoryMessageRunnble implements Runnable {
		private Reader reader;
		private long time;
		public ChannelDestoryMessageRunnble(Reader reader,long time) {
			this.reader = reader;
			this.time=time;
		}

		@Override
		public void run() {
			ChannelHandle.this.channelDestoryHandleByReader(this.reader,this.time);
		}
	}
}
